import mimetypes
import urllib
import string
import settings
import jinja2
import os
import webapp2

from datetime import datetime
from google.appengine.api import users
from google.appengine.ext import db

key_prefix = 'file_'
log_prefix = 'log_'
prefs_prefix = 'prefs_'
segment_size = 10**6
column_names = ['name', 'mime', 'size', 'gets', 'when']

jinja_env = jinja2.Environment(autoescape=True, loader=jinja2.FileSystemLoader(os.path.dirname(__file__) + '/templates/'))

def is_auth():
	try:
		return users.User().email() in settings.auth_emails
	except users.UserNotFoundError:
		return None

def print_default(self):
	values = {
		'site_name': settings.site_name,
		'login_url': users.create_login_url('/'),
		'logout_url': users.create_logout_url('/'),
		'logged_in': is_auth() is not None,
		'user': users.get_current_user(),
	}
	template = jinja_env.get_template('default.html')
	self.response.out.write(template.render(values))

def print_404(self):
	self.error(404)
	values = {
		'error_text': '404 File Not Found',
	}
	template = jinja_env.get_template('error.html')
	self.response.out.write(template.render(values))

def put_log(text, fore = 'black', back = 'transparent'):
	log = LogEntry(key_name = log_prefix + users.User().email())
	log.text = text
	log.fore = fore
	log.back = back
	log.put()

def get_log():
	log = LogEntry.get_by_key_name(log_prefix + users.User().email())

	if log is not None:
		log.delete()
	return log

def get_prefs():
	return Preferences.get_or_insert(prefs_prefix + users.User().email())

def time_since(dt, default='0 seconds ago'):
	diff = datetime.now() - dt

	periods = (
		(diff.days / 365, 'year', 'years'),
		(diff.days / 30, 'month', 'months'),
		(diff.days / 7, 'week', 'weeks'),
		(diff.days, 'day', 'days'),
		(diff.seconds / 3600, 'hour', 'hours'),
		(diff.seconds / 60, 'minute', 'minutes'),
		(diff.seconds, 'second', 'seconds'),
	)

	for period, singular, plural in periods:
		if period:
			return '%d %s ago' % (period, period == 1 and singular or plural)

	return default

def intcomma(val):
	return '{:,d}'.format(val)

jinja_env.filters['intcomma'] = intcomma

class FileSegment(db.Model):
	data = db.BlobProperty()
	next = db.SelfReferenceProperty()

class File(db.Model):
	name = db.StringProperty()
	data = db.BlobProperty()
	seg1 = db.ReferenceProperty(FileSegment)
	mime = db.StringProperty()
	when = db.DateTimeProperty(auto_now_add=True)
	gets = db.IntegerProperty()
	size = db.IntegerProperty()

	def since(self):
		return time_since(self.when)

class LogEntry(db.Model):
	text = db.StringProperty()
	fore = db.StringProperty()
	back = db.StringProperty()

class Preferences(db.Model):
	sort = db.StringProperty(default='-when')

class MainPage(webapp2.RequestHandler):
	def get(self):
		if not is_auth():
			print_default(self)
			return

		prefs = get_prefs()

		sort = self.request.get('sort')

		if sort != '':
			try:
				col = column_names[int(sort)]
				if prefs.sort == col:
					prefs.sort = '-' + col
				else:
					prefs.sort = col
				prefs.put()
			except (ValueError, IndexError):
				pass
			self.redirect('/')
			return

		headers = []
		sort_col = column_names.index(prefs.sort.split('-')[-1])
		sort_dir = prefs.sort.startswith('-') and ' &#x25bc;' or ' &#x25b2;'
		for c, col in enumerate(('Name', 'Type', 'Size', 'Downloads', 'Uploaded')):
			headers.append('%s%s' % (col, c == sort_col and sort_dir or ''))

		files = File.all().order(prefs.sort)

		values = {
			'site_name': settings.site_name,
			'email': users.get_current_user().email(),
			'logout_url': users.create_logout_url('/'),
			'log': get_log(),
			'headers': headers,
			'files': files,
			'num_cols': len(column_names),
		}

		template = jinja_env.get_template('filelist.html')
		self.response.out.write(template.render(values))

class UploadFile(webapp2.RequestHandler):
	def post(self):
		if not is_auth():
			self.redirect('/')
			return

		try:
			filename = self.request.params['upload'].filename

			exists = File.get_by_key_name(key_prefix + filename)

			if exists is not None:
				put_log('Upload failed: a file with that name already exists', 'red')
				self.redirect('/')
				return

			file = File(key_name = key_prefix + filename)

			data = self.request.get('upload')
			index = 0
			datalen = len(data)

			while (datalen - index) > 0:
				seg = FileSegment()
				seg.data = db.Blob(data[index:index + segment_size])
				seg.next = None
				seg.put()
				if file.seg1 is None:
					file.seg1 = seg
					tail = seg
				else:
					tail.next = seg
					tail.put()
					tail = seg
				index += segment_size

			file.name = filename
#			file.data = db.Blob(self.request.get('upload'))
			file.mime = mimetypes.guess_type(file.name, False)[0] or 'application/octet-stream'
			file.gets = 0
			file.size = datalen
			file.put()
			put_log('Uploaded ' + file.name, 'green')
		except Exception:
			put_log('Upload failed!', 'red')

		self.redirect('/')

	def get(self):
		self.redirect('/')

class GetFile(webapp2.RequestHandler):
	def get(self):
		file = File.get_by_key_name(key_prefix + unicode(urllib.unquote(self.request.path[1:]), 'utf-8'))

		if file is None:
			print_404(self)
			return

		file.gets += 1
		file.put()

		self.response.headers['Content-Type'] = str(file.mime)

		if file.data is not None:
			self.response.out.write(file.data)
		else:
			seg = file.seg1
			while seg is not None:
				self.response.out.write(seg.data)
				seg = seg.next

class DeleteFile(webapp2.RequestHandler):
	def get(self):
		if not is_auth():
			self.redirect('/')
			return

		file = File.get_by_key_name(key_prefix + unicode(urllib.unquote(self.request.path[8:]), 'utf-8'))

		if file is None:
			put_log('Delete failed: no such file', 'red')
		else:
			seg = file.seg1
			while seg is not None:
				seg.delete()
				seg = seg.next
			file.delete()
			put_log('Deleted ' + file.name, 'green')

		self.redirect('/')

class RenameFile(webapp2.RequestHandler):
	def get(self):
		if not is_auth():
			self.redirect('/')
			return

		split = string.split(unicode(urllib.unquote(self.request.path), 'utf-8'), '/')

		if len(split) != 4:
			put_log('Rename failed: invalid arguments', 'red')
			self.redirect('/')
			return

		file = File.get_by_key_name(key_prefix + split[2])

		if file is None:
			put_log('Rename failed: no such file', 'red')
			self.redirect('/')
			return

		newname = string.strip(split[3])

		if not newname:
			put_log('Rename failed: invalid name', 'red')
			self.redirect('/')
			return

		exists = File.get_by_key_name(key_prefix + newname)

		if exists is not None:
			put_log('Rename failed: a file with that name already exists', 'red')
			self.redirect('/')
			return

		newfile = File(key_name = key_prefix + newname)

		newfile.name = newname
		newfile.data = file.data
		newfile.seg1 = file.seg1
		newfile.mime = file.mime
		newfile.when = file.when
		newfile.gets = file.gets
		newfile.size = file.size

		file.delete()
		newfile.put()

		put_log('Renamed %s to %s' % (file.name, newfile.name), 'green')

		self.redirect('/')

application = webapp2.WSGIApplication(
	[('/', MainPage),
	('/upload/', UploadFile),
	('/delete/.*', DeleteFile),
	('/rename/.*', RenameFile),
	('/.*', GetFile)],
	debug=True)
